Skip to content

Conversation

@codeconsole
Copy link
Contributor

@codeconsole codeconsole commented Nov 20, 2025

Commands

grails create-domain-class testapp.Book
grails create-domain-class testapp.Car

./gradlew runCommand "-Pargs=create-scaffold-controller testapp.Book"
./gradlew runCommand "-Pargs=create-scaffold-controller testapp.Book --namespace=admin"
./gradlew runCommand "-Pargs=create-scaffold-service testapp.Book"
./gradlew runCommand "-Pargs=generate-scaffold-all testapp.Car"
./gradlew runCommand "-Pargs=create-scaffold-controller testapp.Car --service --namespace=admin"

# With custom superclass
./gradlew runCommand "-Pargs=create-scaffold-service testapp.Book --extends=com.example.MyCustomService"
./gradlew runCommand "-Pargs=create-scaffold-controller testapp.Book --extends=com.example.MyCustomController"
./gradlew runCommand "-Pargs=generate-scaffold-all testapp.Car --serviceExtends=com.example.MyService
--controllerExtends=com.example.MyController"

Generated Files

Domain Classes

testapp/Book.groovy

package testapp

class Book {

    static constraints = {
    }
}

testapp/Car.groovy

package testapp

class Car {

    static constraints = {
    }
}

Controllers

testapp/BookController.groovy (basic)

package testapp

import grails.plugin.scaffolding.annotation.Scaffold

@Scaffold(Book)
class BookController {
}

testapp/admin/BookController.groovy (with namespace)

package testapp.admin

import testapp.Book

import grails.plugin.scaffolding.annotation.Scaffold

@Scaffold(Book)
class BookController {
    static namespace = 'admin'
}

testapp/CarController.groovy (from generate-scaffold-all)

package testapp

import grails.plugin.scaffolding.annotation.Scaffold
import grails.plugin.scaffolding.RestfulServiceController

@Scaffold(RestfulServiceController<Car>)
class CarController {
}

testapp/admin/CarController.groovy (with --service and --namespace)

package testapp.admin

import testapp.Car

import grails.plugin.scaffolding.annotation.Scaffold
import grails.plugin.scaffolding.RestfulServiceController

@Scaffold(RestfulServiceController<Car>)
class CarController {
    static namespace = 'admin'
}

Services

testapp/BookService.groovy

package testapp

import grails.plugin.scaffolding.annotation.Scaffold

@Scaffold(Book)
class BookService {
}

testapp/CarService.groovy (from generate-scaffold-all)

package testapp

import grails.plugin.scaffolding.annotation.Scaffold

@Scaffold(Car)
class CarService {
}

With --extends Flag

testapp/BookService.groovy (custom superclass)

package testapp

import grails.plugin.scaffolding.annotation.Scaffold
import com.example.MyCustomService

@Scaffold(MyCustomService<Book>)
class BookService {
}

testapp/BookController.groovy (custom superclass)

package testapp

import grails.plugin.scaffolding.annotation.Scaffold
import com.example.MyCustomController

@Scaffold(MyCustomController<Book>)
class BookController {
}

Command Options

Command Flag Description
create-scaffold-controller --force Overwrite existing files
--namespace Set the controller namespace
--service Use grails.plugin.scaffolding.RestfulServiceController instead of
grails.rest.RestfulController
--extends Specify a custom superclass (default: grails.rest.RestfulController)
create-scaffold-service --force Overwrite existing files
--extends Specify a custom superclass (default: grails.plugin.scaffolding.GormService)
generate-scaffold-all --force Overwrite existing files
--namespace Set the controller namespace
--serviceExtends Specify a custom superclass for the service (default:
grails.plugin.scaffolding.GormService)
--controllerExtends Specify a custom superclass for the controller (default:
grails.plugin.scaffolding.RestfulServiceController)

@codeconsole codeconsole marked this pull request as draft November 20, 2025 18:22
@codeconsole codeconsole marked this pull request as ready for review November 25, 2025 02:28
Copy link
Contributor

@jdaugherty jdaugherty left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My only concern is there's no documentation for all of this and there's a whole section of the guide related to scaffolding.

@codeconsole codeconsole changed the title 7.1.x scaffold service controller 7.1.x @Scaffold Services and Controllers Nov 29, 2025
@codeconsole codeconsole force-pushed the 7.1.x-scaffold-service-controller branch from 3fbe3e2 to bb6540a Compare November 29, 2025 18:06
Copy link
Contributor

@sbglasius sbglasius left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks very good (I'm going to use it) and is very well documented.

Thank you @codeconsole

@matrei
Copy link
Contributor

matrei commented Dec 1, 2025

Look good @codeconsole. Do we need tests?
Some comments below.


I find the DisplayType values a bit unintuitive. Suggestion:

DisplayType.ALL -> DisplayType.ALWAYS
DisplayType.NONE -> DisplayType.NEVER
DisplayType.INPUT_ONLY -> DisplayType.WRITE_ONLY
DisplayType.OUTPUT_ONLY -> DisplayType.READ_ONLY

Aren't the command names and usage instructions inconsistent and confusing (I have not used scaffolding much myself)?
When to use create-*, create-scaffold-* or generate-*?
I understand this is not caused by this PR, but I think we should overhaul this for Grails 8.

Now added to the mix:

grails create-scaffold-controller Book
grails create-scaffold-service Book
grails generate-scaffold-all Book

Does create-scaffold-controller break compatibility with existing command below?

From 7.0.3 grails help:

Command Description Usage instruction
create-controller Creates a controller create-controller [controller name]
create-domain-class Creates a Domain Class create-domain-class [domain class name]
create-scaffold-controller Creates a scaffolded controller create-controller [controller name]
create-service Creates a Service create-service [service name]
generate-all Generates a controller that performs CRUD operations and the associated views grails generate-all [DOMAIN CLASS]
generate-async-controller Generates an asynchronous controller that performs CRUD operations grails generate-async-controller [DOMAIN CLASS]
generate-controller Generates a controller that performs CRUD operations grails generate-controller [DOMAIN CLASS]
generate-service Generates a Grails data service for the specified domain-class. No usage instruction
generate-views Generates GSP views for the specified domain class `grails generate-views [DOMAIN CLASS]

I think it’s a good idea to start removing the @author tags, but then we should also refrain from adding new ones.

@codeconsole
Copy link
Contributor Author

@matrei Unless we commit to a policy of maintaining source history and restore the previously deleted history, I feel @author tags are necessary

@codeconsole
Copy link
Contributor Author

codeconsole commented Dec 1, 2025

create-scaffold-controller wasn't added. It should work how it did previously. All that was changed was adding optional parameters.

create-scaffold-service mimics in functionality create-scaffold-controller but in relation to services instead of controllers.

generate-scaffold-all is the equivalent to generate-all. It generates the scaffolded equivalent.

[source,groovy]
----
class BookController {
static scaffold = Book // Legacy syntax - @Scaffold annotation is preferred
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could change the AST transform to indicate it's "legacy" so it outputs a warning. Longer term this would allow us to remove the property and keep the api clean.

@jdaugherty
Copy link
Contributor

@matrei Unless we commit to a policy of maintaining source history and restore the previously deleted history, I feel @author tags are necessary

From when we were in incubation, we learned that one of the major policies of the ASF is to not have authorship of individual files. They even encourage removing @author. The line of thinking is that individual authorship discourages contribution to a specific file because someone else is seen as an author. Longer term, we will have to remove these. Our first step is adding all contributors to the pom files as authors and handling this at the project level. There is currently a discussion underway on the mailing list about how best to accomplish this: https://lists.apache.org/thread/c33fx3djh17tl67y0g3szhvsq98cq2jc

@jamesfredley FYI

@jamesfredley
Copy link
Contributor

@codeconsole codeconsole merged commit e9e015c into apache:7.1.x Dec 2, 2025
32 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

5 participants